﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Cluster.LiveJournalLib
{
    public class LiveJournalClient : LiveJournal
    {
        public readonly string ClientName;
        protected string defaultPictureFile = "";
        public string DefaultPictureFile
        {
            get { return defaultPictureFile; }
        }
        protected IDictionary<string, string> menuData = new Dictionary<string, string>();

        public LiveJournalClient(string clientName)
        {
            ClientName = clientName;
        }

        public void SavePassword()
        {
            if (!UserExists(user)) return;
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user, true) ??
                      Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user);
            if (key != null)
            {
                key.SetValue("Password", hPassword, RegistryValueKind.String);
            }
        }

        public void LoadPassword()
        {
            if (!UserExists(user)) return;
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user, true) ??
                      Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user);
            if (key != null)
            {
                var v = key.GetValue("Password");
                if (v != null && ((string) v != GetMD5Hash(""))) hPassword = (string) v;
                else throw new Exception("Password is not saved");
            }
        }

        public bool HasSavedPassword()
        {
            return HasSavedPassword(user);
        }
        public bool HasSavedPassword(string userName)
        {
            if (!UserExists(userName)) return false;
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + userName, true) ??
                      Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + userName);
            if (key != null)
            {
                var v = key.GetValue("Password");
                return (v != null && ((string) v != GetMD5Hash("")));
            }
            return false;
        }

        public void UnsavePassword()
        {
            UnsavePassword(user);
        }
        public void UnsavePassword(string userName)
        {
            if (!UserExists(userName)) return;
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + userName, true) ??
                      Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + userName);
            if (key != null)
            {
                try
                {
                    key.DeleteValue("Password");
                }
                catch
                {
                }
            }
        }

        public string[] GetUserList()
        {
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName, true);
            if (key != null)
            {
                var value = key.GetValue("UserNames");
                if ((value != null) && ((string)value != ""))
                {
                     return ((string)value).Split(new[] { ',' });
                }
            }
            return new string[] {};
        }
        public string GetUserPic(string userName)
        {
            var key =
                Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + userName + "\\Pictures",
                                                true);
            if (key != null)
            {
                var value = key.GetValue("Default_file");
                return (string) (value ?? "");
            }
            return "";
        }

        public bool UserExists(string userName)
        {
            var users = GetUserList();
            foreach (var storedUser in users) if (storedUser == userName) return true;
            return false;
        }

        public bool AddUser()
        {
            bool result = false;
            if (!UserExists(user))
            {
                var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName, true) ??
                          Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName);
                if (key != null)
                {
                    var value = (string) key.GetValue("UserNames");
                    value += (!string.IsNullOrEmpty(value) ? "," : "") + user;
                    key.SetValue("UserNames", value, RegistryValueKind.String);
                }
                result = true;
            }
            return result;
        }

        public void RemoveUser(string userName)
        {
            UnsavePassword(userName);
            var key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName, true);
            if (key != null)
            {
                string[] users = ((string) (key.GetValue("UserNames") ?? "")).Split(new[] {','});
                if ((users.Length == 1) && (users[0] == "")) users = new string[] { };
                var usersList = new List<string>(users);
                usersList.Remove(userName);
                key.SetValue("UserNames", string.Join(",", usersList.ToArray()), RegistryValueKind.String);
            }
            try
            {
                Directory.Delete(GetUserDataPath(userName), true);
            }
            catch
            {
            }
        }
   
        public void LoadData(bool forceUpdate)
        {
            if (forceUpdate)
            {
                Login(true, true, true, true);
                LoadTags();
                LoadFriends(true, true, true, null);
                AddUser();
            }
            else
            {
                var key =
                    Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user, true);
                if (key != null)
                {
                    var value = key.GetValue("Name");
                    name = (string) (value ?? "");
                    value = key.GetValue("Journals");
                    journals = ((string)(value ?? "")).Split(new[] { ',' });
                    if ((journals.Length == 1) && (journals[0] == "")) journals = new string[] {};
                    value = key.GetValue("FastServer");
                    fastServer = ((value != null) && ((int) value == 1));
                    value = key.GetValue("Tags");
                    tags = ((string) (value ?? "")).Split(new[] {','});
                    if ((tags.Length == 1) && (tags[0] == "")) tags = new string[] { };

                    key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Moods",
                                                          true);
                    moods.Clear();
                    if (key != null)
                    {
                        value = key.GetValue("Count");
                        var count = (int) (value ?? 0);
                        for (int c = 0; c < count; c++)
                        {
                            var moodId = key.GetValue("mood_" + c + "_id");
                            var moodName = key.GetValue("mood_" + c + "_name");
                            if ((moodId != null) && (moodName != null))
                                moods[(uint) (int) moodId] = (string) moodName;
                        }
                    }

                    key = Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Menu",
                                                          true);
                    menuData.Clear();
                    if (key != null)
                    {
                        foreach (var k in key.GetValueNames())
                        {
                            value = key.GetValue(k);
                            if (value != null) menuData[k] = (string) value;
                        }
                    }
                    liveJournalMenu = ParseMenu(menuData, 0);

                    key =
                        Registry.CurrentUser.OpenSubKey(
                            "Software\\Cluster\\" + ClientName + "\\" + user + "\\Pictures", true);
                    pictures = new PictureInfo[] {};
                    var picturesList = new List<PictureInfo>();
                    if (key != null)
                    {
                        value = key.GetValue("Count");
                        var count = (int) (value ?? 0);
                        defaultPictureUrl = (string) (key.GetValue("default_url") ?? "");
                        defaultPictureFile = (string) (key.GetValue("default_file") ?? "");
                        for (int c = 0; c < count; c++)
                        {
                            var picture = new PictureInfo();
                            var valueKey = key.GetValue("picture_" + c + "_keyword");
                            var valueUrl = key.GetValue("picture_" + c + "_url");
                            var valueFile = key.GetValue("picture_" + c + "_file");
                            if (valueKey != null)
                            {
                                picture.keyword = (string) valueKey;
                                picture.url = (string) valueUrl;
                                picture.fileName = (string) valueFile;
                                picturesList.Add(picture);
                            }
                        }
                    }
                    pictures = picturesList.ToArray();

                    key =
                        Registry.CurrentUser.OpenSubKey(
                            "Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendGroups", true);
                    var friendGroupsList = new List<FriendGroup>();
                    if (key != null)
                    {
                        value = key.GetValue("Count");
                        var count = (int) (value ?? 0);
                        for (int c = 0; c < count; c++)
                        {
                            var group = new FriendGroup();
                            var valueId = key.GetValue("group_" + c + "_id");
                            var valueName = key.GetValue("group_" + c + "_name");
                            var valueSort = key.GetValue("group_" + c + "_sortorder");
                            var valuePublic = key.GetValue("group_" + c + "_public");
                            if ((valueId != null) && (valueName != null))
                            {
                                group.id = (uint) (int) valueId;
                                group.name = (string) valueName;
                                group.sortorder = (uint) (int) (valueSort ?? 0);
                                group.isPublic = ((valuePublic != null) && ((int) valuePublic != 0));
                                friendGroupsList.Add(group);
                            }
                        }
                    }
                    friendGroups = friendGroupsList.ToArray();

                    key = Registry.CurrentUser.OpenSubKey(
                        "Software\\Cluster\\" + ClientName + "\\" + user + "\\Friends", true);
                    var friendsList = new List<FriendInfo>();
                    if (key != null)
                    {
                        value = key.GetValue("Count");
                        var count = (int) (value ?? 0);
                        for (int c = 0; c < count; c++)
                        {
                            var friend = new FriendInfo();
                            var valueUserName = key.GetValue("friend_" + c + "_username");
                            var valueName = key.GetValue("friend_" + c + "_name");
                            var valueGroupMask = key.GetValue("friend_" + c + "_groupmask");
                            var valueBirthday = key.GetValue("friend_" + c + "_birthday");
                            var valueType = key.GetValue("friend_" + c + "_type");
                            var valueStatus = key.GetValue("friend_" + c + "_status");
                            var fgColor = key.GetValue("friend_" + c + "_fg");
                            var bgColor = key.GetValue("friend_" + c + "_bg");
                            if ((valueUserName != null) && (valueGroupMask != null)
                                && (valueType != null) && (valueStatus != null))
                            {
                                friend.userName = (string) valueUserName;
                                friend.name = (string) (valueName ?? "");
                                friend.groupMask = (uint) (int) valueGroupMask;
                                friend.birthday = (string) (valueBirthday ?? "");
                                friend.friendType = GetFriendType((string) valueType);
                                friend.friendStatus = GetFriendStatus((string) valueStatus);
                                if (fgColor != null) friend.foregroundColor = Color.FromArgb((int)fgColor);
                                if (bgColor != null) friend.backgroundColor = Color.FromArgb((int)bgColor);
                                friendsList.Add(friend);
                            }
                        }
                    }
                    friends = friendsList.ToArray();

                    key =
                        Registry.CurrentUser.OpenSubKey(
                            "Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendOf", true);
                    var friendOfList = new List<FriendInfo>();
                    if (key != null)
                    {
                        value = key.GetValue("Count");
                        var count = (int) (value ?? 0);
                        for (int c = 0; c < count; c++)
                        {
                            var friend = new FriendInfo();
                            var valueUserName = key.GetValue("friend_" + c + "_username");
                            var valueName = key.GetValue("friend_" + c + "_name");
                            var valueType = key.GetValue("friend_" + c + "_type");
                            var valueStatus = key.GetValue("friend_" + c + "_status");
                            var fgColor = key.GetValue("friend_" + c + "_fg");
                            var bgColor = key.GetValue("friend_" + c + "_bg");
                            if ((valueUserName != null) && (valueType != null) && (valueStatus != null))
                            {
                                friend.userName = (string) valueUserName;
                                friend.name = (string) (valueName ?? "");
                                friend.friendType = GetFriendType((string) valueType);
                                friend.friendStatus = GetFriendStatus((string) valueStatus);
                                if (fgColor != null) friend.foregroundColor = Color.FromArgb((int)fgColor);
                                if (bgColor != null) friend.backgroundColor = Color.FromArgb((int)bgColor);
                                friendOfList.Add(friend);
                            }
                        }
                    }
                    friendOf = friendOfList.ToArray();
                }
            }
        }

        protected override LiveJournalMenuItem[] ParseMenu(IDictionary<string, string> parameters, uint menuId)
        {
            if (menuId == 0)
            {
                menuData = new Dictionary<string, string>();
                foreach (var key in parameters.Keys)
                {
                    if (key.IndexOf("menu_") == 0) menuData[key] = parameters[key];
                }
            }
            return base.ParseMenu(parameters, menuId);
        }

        private string GetUserDataPath()
        {
            return GetUserDataPath(user);
        }
        private string GetUserDataPath(string userName)
        {
            string dir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            dir = Path.Combine(dir, ClientName);
            dir = Path.Combine(dir, userName);
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            return dir;
        }

        protected string DownloadFile(String remoteFilename,
                                      String localFilename)
        {
            Stream remoteStream = null;
            Stream localStream = null;
            WebResponse response = null;
            try
            {
                WebRequest request = WebRequest.Create(remoteFilename);
                response = request.GetResponse();
                switch (response.ContentType)
                {
                    case "image/jpeg":
                        localFilename += ".jpg";
                        break;
                    case "image/gif":
                        localFilename += ".gif";
                        break;
                    case "image/png":
                        localFilename += ".png";
                        break;
                }
                remoteStream = response.GetResponseStream();
                localStream = File.Create(localFilename);
                var buffer = new byte[1024];
                int bytesRead;
                do
                {
                    bytesRead = remoteStream.Read(buffer, 0, buffer.Length);
                    localStream.Write(buffer, 0, bytesRead);
                    bytesInCount += (uint) bytesRead;
                    Application.DoEvents();
                } while (bytesRead > 0);
            }

            finally
            {
                if (response != null) response.Close();
                if (remoteStream != null) remoteStream.Close();
                if (localStream != null) localStream.Close();
            }
            return localFilename;
        }

        protected static FriendType GetFriendType(string text)
        {
            switch (text)
            {
                case "Normal":
                    return FriendType.Normal;
                case "Community":
                    return FriendType.Community;
                case "Identity":
                    return FriendType.Identity;
                case "News":
                    return FriendType.News;
                case "Shared":
                    return FriendType.Shared;
                default:
                    throw new Exception("Unknown friend type");
            }
        }

        protected static FriendStatus GetFriendStatus(string text)
        {
            switch (text)
            {
                case "Normal":
                    return FriendStatus.Normal;
                case "Purged":
                    return FriendStatus.Purged;
                case "Deleted":
                    return FriendStatus.Deleted;
                case "Suspended":
                    return FriendStatus.Suspended;
                default:
                    throw new Exception("Unknown friend status");
            }
        }

        public override string Login(bool getmoods, bool getmenus, bool getpickws, bool getpickwurls)
        {
            var message = base.Login(getmoods, getmenus, getpickws, getpickwurls);
            var key =
                Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user, true) ??
                Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user);
            if (key != null)
            {
                key.SetValue("Name", name, RegistryValueKind.String);
                key.SetValue("Journals", string.Join(",", journals), RegistryValueKind.String);
                key.SetValue("FastServer", fastServer ? 1 : 0, RegistryValueKind.DWord);
            }

            // Saving moods...
            if (getmoods)
            {
                key =
                    Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Moods", true) ??
                    Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Moods");
                if (key != null)
                {
                    key.SetValue("Count", moods.Count, RegistryValueKind.DWord);
                    int c = 0;
                    foreach (var moodId in moods.Keys)
                    {
                        key.SetValue("mood_" + c + "_id", moodId, RegistryValueKind.DWord);
                        key.SetValue("mood_" + c + "_name", moods[moodId], RegistryValueKind.String);
                        c++;
                    }
                }
            }
            if (getmenus)
            {
                key =
                    Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Menu", true) ??
                    Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Menu");
                if (key != null)
                {
                    foreach (var menuId in menuData.Keys)
                    {
                        key.SetValue(menuId, menuData[menuId], RegistryValueKind.String);
                    }
                }
            }
            if (getpickws || getpickwurls)
            {
                key =
                    Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Pictures",
                                                    true) ??
                    Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Pictures");
                if (key != null)
                {
                    key.SetValue("Count", pictures.Length, RegistryValueKind.DWord);
                    string appDir = GetUserDataPath();
                    if (getpickwurls)
                    {
                        key.SetValue("default_url", defaultPictureUrl, RegistryValueKind.String);
                        string localFile = Path.Combine(appDir, user + "_default");
                        localFile = DownloadFile(defaultPictureUrl, localFile);
                        key.SetValue("default_file", localFile, RegistryValueKind.String);
                        defaultPictureFile = localFile;
                    }
                    for (int c = 0; c < pictures.Length; c++)
                    {
                        if (getpickws)
                            key.SetValue("picture_" + c + "_keyword", pictures[c].keyword,
                                         RegistryValueKind.String);
                        if (getpickwurls)
                        {
                            key.SetValue("picture_" + c + "_url", pictures[c].url, RegistryValueKind.String);
                            try
                            {
                                string localFile = Path.Combine(appDir, user + "_" + c);
                                localFile = DownloadFile(pictures[c].url, localFile);
                                key.SetValue("picture_" + c + "_file", localFile, RegistryValueKind.String);
                                pictures[c].fileName = localFile;
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
            return message;
        }

        public override void LoadTags()
        {
            base.LoadTags();
            {
                var key =
                    Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user,
                                                    true) ??
                    Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user);
                if (key != null)
                {
                    key.SetValue("Tags", string.Join(",", tags), RegistryValueKind.String);
                }
            }
        }

        public override void LoadFriends(bool includeFriendOf, bool includeGroups, bool includeBirthdays, uint? limit)
        {
            base.LoadFriends(includeFriendOf, includeGroups, includeBirthdays, limit);
            var key =
                Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Friends", true) ??
                Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\Friends");
            if (key != null)
            {
                key.SetValue("Count", friends.Length, RegistryValueKind.DWord);
                for (int c = 0; c < friends.Length; c++)
                {
                    key.SetValue("friend_" + c + "_username", friends[c].userName, RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_name", friends[c].name, RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_groupmask", friends[c].groupMask, RegistryValueKind.DWord);
                    if (friends[c].birthday != null)
                        key.SetValue("friend_" + c + "_birthday", friends[c].birthday, RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_type", friends[c].friendType.ToString(), RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_status", friends[c].friendStatus.ToString(), RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_fg", friends[c].foregroundColor.ToArgb(), RegistryValueKind.DWord);
                    key.SetValue("friend_" + c + "_bg", friends[c].backgroundColor.ToArgb(), RegistryValueKind.DWord);
                }
            }
        }

        protected override FriendGroup[] ParseFriendGroups(IDictionary<string, string> answer)
        {
            var groups = base.ParseFriendGroups(answer);
            var key =
                Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendGroups",
                                                true) ??
                Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendGroups");
            if (key != null)
            {
                key.SetValue("Count", groups.Length, RegistryValueKind.DWord);
                for (int c = 0; c < groups.Length; c++)
                {
                    key.SetValue("group_" + c + "_id", groups[c].id, RegistryValueKind.DWord);
                    key.SetValue("group_" + c + "_name", groups[c].name, RegistryValueKind.String);
                    key.SetValue("group_" + c + "_sortorder", groups[c].sortorder, RegistryValueKind.DWord);
                    key.SetValue("group_" + c + "_ispublic", groups[c].isPublic ? 1 : 0, RegistryValueKind.DWord);
                }
            }
            return groups;
        }

        protected override FriendInfo[] ParseFriendOf(IDictionary<string, string> answer)
        {
            var friendOfList = base.ParseFriendOf(answer);
            var key =
                Registry.CurrentUser.OpenSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendOf", true) ??
                Registry.CurrentUser.CreateSubKey("Software\\Cluster\\" + ClientName + "\\" + user + "\\FriendOf");
            if (key != null)
            {
                key.SetValue("Count", friendOfList.Length, RegistryValueKind.DWord);
                for (int c = 0; c < friendOfList.Length; c++)
                {
                    key.SetValue("friend_" + c + "_username", friendOfList[c].userName, RegistryValueKind.String);
                    if (friendOfList[c].name != null)
                        key.SetValue("friend_" + c + "_name", friendOfList[c].name, RegistryValueKind.String);
                    if (friendOfList[c].birthday != null)
                        key.SetValue("friend_" + c + "_birthday", friendOfList[c].birthday, RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_type", friendOfList[c].friendType.ToString(),
                                 RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_status", friendOfList[c].friendStatus.ToString(),
                                 RegistryValueKind.String);
                    key.SetValue("friend_" + c + "_fg", friendOfList[c].foregroundColor.ToArgb(), RegistryValueKind.DWord);
                    key.SetValue("friend_" + c + "_bg", friendOfList[c].backgroundColor.ToArgb(), RegistryValueKind.DWord);
                }
            }
            return friendOfList;
        }
    }
}